#include "TCPServer.h"
#include <QTcpServer>
#include <QRegExpValidator>
#include <QRegExp>
#include <QTcpSocket>
#include <QDateTime>
#include <QApplication>

#include <QLineEdit>
#include <QGroupBox>
#include <QPushButton>
#include <QGridLayout>
#include <QLabel>
#include <QComboBox>
#include <QTextEdit>
#include <QNetworkInterface>
#include <QDebug>
#include <QCheckBox>
#include <QSpinBox>
#include <QTimer>

TCPServer::TCPServer(QWidget *parent) : QWidget(parent)
{
    initWidget();

    m_netSettings.sendHex = false;
    m_netSettings.showHex = false;
    m_netSettings.showTime = false;

    m_autoSendTimer = new QTimer(this);
    connect(m_autoSendTimer, &QTimer::timeout, this, &TCPServer::slotBtnClickedSend);

    m_tcpServer = new QTcpServer(this);
    connect(m_tcpServer, &QTcpServer::newConnection, this, &TCPServer::newConnection);

    slotWordWrap(false);
    updateControlState();
}

TCPServer::~TCPServer()
{
    if(m_tcpServer && m_tcpServer->isListening()){
        QList<QTcpSocket*> clientList = mapTcpClients.values();
        for(int i=0; i<clientList.count(); i++)
        {
            clientList.at(i)->disconnectFromHost();
        }
        m_tcpServer->close();
        m_tcpServer->deleteLater();
    }
}

QString TCPServer::getCurrentIPV4()
{
    QList<QNetworkInterface> interfaceList = QNetworkInterface::allInterfaces();
    for(int i=0; i<interfaceList.length(); i++){
        if(!interfaceList.at(i).flags().testFlag(QNetworkInterface::IsLoopBack) &&
                interfaceList.at(i).flags().testFlag(QNetworkInterface::IsUp) &&
                interfaceList.at(i).flags().testFlag(QNetworkInterface::IsRunning)){
            if(interfaceList.at(i).humanReadableName().contains(QRegExp("(VirtualBox)|(VMware)"))){
                continue;
            }
            QList<QNetworkAddressEntry> netEntryList = interfaceList.at(i).addressEntries();
            QNetworkAddressEntry entry;
            for(int i=0; i<netEntryList.length(); i++){
                entry = netEntryList.at(i);
                if(netEntryList.at(i).ip().protocol() == QAbstractSocket::IPv4Protocol){
                    return entry.ip().toString();
                }
            }
        }
    }
    return "127.0.0.1";
}

void TCPServer::initWidget()
{
    QGroupBox *group_netSet = new QGroupBox("Server Settings", this);
    QLabel *lb_serverIP = new QLabel("Server IP", this);
    m_ctrServerIP = new QComboBox(this);
    QLabel *lb_serverPort = new QLabel("Server Port", this);
    m_ctrServerPort = new QLineEdit(this);
    m_btnListen = new QPushButton("Listen", this);

    m_ctrServerPort->setValidator(new QRegExpValidator(QRegExp("^\\d{1,6}$")));
    m_ctrServerPort->setText("1234");
    m_ctrServerIP->addItem(getCurrentIPV4());

    QGridLayout *layout_netSet = new QGridLayout(group_netSet);
    layout_netSet->addWidget(lb_serverIP, 0, 0);
    layout_netSet->addWidget(m_ctrServerIP, 0, 1);
    layout_netSet->addWidget(lb_serverPort, 1, 0);
    layout_netSet->addWidget(m_ctrServerPort, 1, 1);
    layout_netSet->addWidget(m_btnListen, 2, 0, 1, 2, Qt::AlignCenter);

    QGroupBox *group_recvSet = new QGroupBox("Recieve Settings", this);
    QGridLayout *layout_recvSet = new QGridLayout(group_recvSet);
    QCheckBox *check_hexRecv = new QCheckBox("Hex Recieve", this);
    QCheckBox *check_showTime = new QCheckBox("Show Time", this);
    QCheckBox *check_wordWrap = new QCheckBox("Word Wrap", this);
    layout_recvSet->addWidget(check_showTime, 0, 0);
    layout_recvSet->addWidget(check_hexRecv, 1, 0);
    layout_recvSet->addWidget(check_wordWrap, 2, 0);

    QGroupBox *group_sendSet = new QGroupBox("Send Settings", this);
    QGridLayout *layout_sendSet = new QGridLayout(group_sendSet);
    QCheckBox *check_hexSend = new QCheckBox("Hex Send", this);
    QCheckBox *check_autoSend = new QCheckBox("Auto Send", this);
    m_spinBoxSendMs = new QSpinBox(this);
    m_spinBoxSendMs->setSuffix(" ms");
    m_spinBoxSendMs->setRange(1, 9999);
    m_spinBoxSendMs->setValue(1000);
    layout_sendSet->addWidget(check_hexSend, 0, 0, 1, 2);
    layout_sendSet->addWidget(check_autoSend, 1, 0, 1, 1);
    layout_sendSet->addWidget(m_spinBoxSendMs, 1, 1, 1, 1);

    QPushButton *btn_clearRecv = new QPushButton("Clear Recieve", this);
    QPushButton *btn_clearSend = new QPushButton("Clear Send", this);
    QHBoxLayout *layout_clearBtns = new QHBoxLayout;
    layout_clearBtns->addWidget(btn_clearRecv);
    layout_clearBtns->addWidget(btn_clearSend);

    QVBoxLayout *layout_settings = new QVBoxLayout;
    layout_settings->addWidget(group_netSet);
    layout_settings->addWidget(group_recvSet);
    layout_settings->addWidget(group_sendSet);
    layout_settings->addLayout(layout_clearBtns);
    layout_settings->addStretch(1);

    QGroupBox *group_recv = new QGroupBox("Recieve Data", this);
    QGridLayout *layout_recv = new QGridLayout(group_recv);
    m_textRecv = new QTextEdit(this);
    layout_recv->addWidget(m_textRecv);
    m_textRecv->setReadOnly(true);

    QGroupBox *group_send = new QGroupBox("Send Data", this);
    QGridLayout *layout_send = new QGridLayout(group_send);
    QLabel *lb_Clients = new QLabel("Clients", this);
    m_comboxClients = new QComboBox(this);
    m_textSend = new QTextEdit(this);
    m_btnSend = new QPushButton("Send", this);
    layout_send->addWidget(lb_Clients, 0, 0);
    layout_send->addWidget(m_comboxClients, 0, 1);
    layout_send->addWidget(m_textSend, 1, 0, 1, 2);
    layout_send->addWidget(m_btnSend, 1, 2);
    layout_send->setColumnStretch(1, 1);
    m_textSend->setMaximumHeight(80);

    QGridLayout *layout_surface = new QGridLayout(this);
    layout_surface->addLayout(layout_settings, 0, 0, 2, 1);
    layout_surface->addWidget(group_recv, 0, 1, 1, 1);
    layout_surface->addWidget(group_send, 1, 1, 1, 1);
    layout_surface->setRowStretch(0, 1);
    layout_surface->setColumnStretch(1, 1);

    connect(m_btnListen, &QPushButton::clicked, this, &TCPServer::slotBtnClickedListen);
    connect(m_btnSend, &QPushButton::clicked, this, &TCPServer::slotBtnClickedSend);
    connect(check_hexRecv, &QCheckBox::stateChanged, this, &TCPServer::slotHexRecieve);
    connect(check_showTime, &QCheckBox::stateChanged, this, &TCPServer::slotShowTime);
    connect(check_wordWrap, &QCheckBox::stateChanged, this, &TCPServer::slotWordWrap);
    connect(check_hexSend, &QCheckBox::stateChanged, this, &TCPServer::slotHexSend);
    connect(check_autoSend, &QCheckBox::stateChanged, this, &TCPServer::slotAutoSend);
    connect(btn_clearRecv, &QPushButton::clicked, m_textRecv, &QTextEdit::clear);
    connect(btn_clearSend, &QPushButton::clicked, m_textSend, &QTextEdit::clear);
}

void TCPServer::addLog(QString text)
{
    if(m_netSettings.showTime){
        text = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz : ") + text;
    }
    m_textRecv->append(text);
}

void TCPServer::slotBtnClickedListen()
{
    if(m_tcpServer->isListening()){
        m_tcpServer->close();
        QList<QTcpSocket*> clientList = mapTcpClients.values();
        for(int i=0; i<clientList.count(); i++)
        {
            clientList.at(i)->disconnectFromHost();
        }
        mapTcpClients.clear();
    }
    else{
        QString strIP = m_ctrServerIP->currentText().simplified();
        QString strPort = m_ctrServerPort->text().simplified();
        if(strIP.isEmpty()){
            addLog("Server IP Is Empty");
            return;
        }
        if(strPort.isEmpty()){
            addLog("Server Port Is Empty");
            return;
        }

        if(m_tcpServer->listen(QHostAddress(strIP), strPort.toUInt())){
            addLog("Start Listing");
            emit signalUpdateTitle(QString("TCPServer-%1:%2").arg(strIP).arg(strPort));
        }
        else{
            addLog("Listen Failed");
        }
    }
    updateControlState();
}

void TCPServer::updateControlState()
{
    bool state = m_tcpServer->isListening();
    m_btnListen->setText(state ? "Close" : "Listen");
    m_ctrServerIP->setEnabled(!state);
    m_ctrServerPort->setEnabled(!state);
    m_btnSend->setEnabled(state);
}

void TCPServer::slotBtnClickedSend()
{
    QString text = m_textSend->toPlainText();
    if(text.isEmpty()){
        return;
    }

    QByteArray data = text.toLocal8Bit();
    if(m_netSettings.sendHex){
        data = QByteArray::fromHex(data);
    }

    if(m_comboxClients->currentIndex() == 0)
    {
        QList<QTcpSocket*> clientList = mapTcpClients.values();
        for(int i=0; i<clientList.count(); i++)
        {
            clientList.at(i)->write(data);
            clientList.at(i)->flush();
            addLog("send data to client: " + mapTcpClients.key(clientList.at(i)) + " : " + text);
        }
    }
    else
    {
        QTcpSocket* client = mapTcpClients.value(m_comboxClients->currentText());
        client->write(data);
        client->flush();
        addLog("send data to client: " + mapTcpClients.key(client) + " : " + text);
    }
}

void TCPServer::slotHexRecieve(int state)
{
    m_netSettings.showHex = (Qt::Checked == state);
}

void TCPServer::slotShowTime(int state)
{
    m_netSettings.showTime = (Qt::Checked == state);
}

void TCPServer::slotWordWrap(int state)
{
    m_textRecv->setWordWrapMode(Qt::Checked == state ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
}

void TCPServer::slotHexSend(int state)
{
    m_netSettings.sendHex = (Qt::Checked == state);
}

void TCPServer::slotAutoSend(int state)
{
    bool autoSend(Qt::Checked == state);
    if(autoSend){
        m_autoSendTimer->setInterval(m_spinBoxSendMs->value());
        m_autoSendTimer->start();
    }
    else{
        m_autoSendTimer->stop();
    }
    m_spinBoxSendMs->setEnabled(!autoSend);
}

void TCPServer::newConnection()
{
    QTcpSocket *client = m_tcpServer->nextPendingConnection();
    QString clientInfo = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());
    mapTcpClients.insert(clientInfo, client);

    m_comboxClients->clear();
    m_comboxClients->addItem("All clients");
    m_comboxClients->addItems(mapTcpClients.keys());

    connect(client, &QTcpSocket::readyRead, this, &TCPServer::slotReadyRead);
    connect(client, &QTcpSocket::disconnected, this, &TCPServer::slotDisconnected);
}

void TCPServer::slotReadyRead()
{
    QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());
    QByteArray data = socket->readAll();

    if(m_netSettings.showHex){
        addLog(mapTcpClients.key(socket) + " : " + data.toHex(' '));
    }
    else{
        addLog(mapTcpClients.key(socket) + " : " + QString::fromLocal8Bit(data));
    }
}

void TCPServer::slotDisconnected()
{
    QString clientName = mapTcpClients.key(qobject_cast<QTcpSocket*>(sender()));
    mapTcpClients.remove(clientName);
    if(!mapTcpClients.isEmpty()){
        m_comboxClients->clear();
        m_comboxClients->addItem("All clients");
        m_comboxClients->addItems(mapTcpClients.keys());
    }
    addLog(clientName + " disconnected");
}
